﻿#include <QtCore/QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QTextCodec>
#ifndef QT_NO_DEBUG
#include <qdebug.h>
#endif // QT_ON_DEBUG
#include <QDir>
#include "qdiriterator.h"
#include "qmap.h"

//默认步骤:为注释部分添加新行并去掉注释
//原注释部分添加间隔符号| 新增部分*$/转换为*/
int nowStep = 1;

struct fileAnnotation {
	QString suffix;			 //扩展名
	QString muxLineStartUp;  //多行注释头向上添加 /*<!!-  第一列
	QString muxLineStartDown;//多行注释头向下添加 /*<!--  第二列
	QString muxLineEnd;		 //多行注释尾	    	-->*/ 第三列
	QString oneLineStartUp;  //单行注释向上添加  //<!!-   第四列
	QString oneLineStartDown;//单行注释向下添加 //<!--   第五列
	QString oneLineEnd;		 //单行注释结束符号 -->      第六列
	QString endOld;			//多行原结尾   */			 第七列
	QString endNow;			//多行转义结尾 *$/			 第八列
	bool BOM = false;	    //输入输出文件是否包含BOM头
	QString enableStr;		//起效关键字   #ball
};

std::map<QString, fileAnnotation> fileinfo;//所有格式的处理方式
fileAnnotation nowFileInfo;//当前格式的处理方式

void getFileInfo(QString fileName) {
	fileinfo.clear();
	//1.提取当前目录
	QString currentPath = QCoreApplication::applicationDirPath();
	//2.读取csv文件
	QFile file(fileName);
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
		nowFileInfo.suffix = "cpp";
		nowFileInfo.muxLineStartDown = "/*<!--";
		nowFileInfo.muxLineStartUp = "/*<!!-";
		nowFileInfo.muxLineEnd = "-->*/";
		nowFileInfo.oneLineStartDown = "//<!--";
		nowFileInfo.oneLineStartUp = "//<!!-";
		nowFileInfo.oneLineEnd = "-->";
		nowFileInfo.endOld = "*/";
		nowFileInfo.endNow = "*$/";
		nowFileInfo.BOM = false;
		fileinfo.insert(std::pair<QString, fileAnnotation>("cpp", nowFileInfo));
		nowFileInfo.suffix = "h";
		fileinfo.insert(std::pair<QString, fileAnnotation>("h", nowFileInfo));
		return;
	}
	QStringList list;
	list.clear();
	QTextStream in(&file);
	int i = 0;
	while (!in.atEnd()) {
		fileAnnotation temp;
		QString fileLine = in.readLine();
		list = fileLine.split(",");
		if (i >= 1) {
			for (int a = 0; a <= 10; a++) {
				if (a >= list.size())break;
				if (a == 0)temp.suffix = list.at(a);
				if (a == 1)temp.muxLineStartDown = list.at(a);//多行注释头向下添加 /*<!--  第一列
				if (a == 2)temp.muxLineStartUp = list.at(a);  //多行注释头向上添加 /*<!!-  第二列
				if (a == 3)temp.muxLineEnd = list.at(a);	  //多行注释尾	    	-->*/ 第三列
				if (a == 4)temp.oneLineStartDown = list.at(a);//单行注释向下添加 //<!--   第四列
				if (a == 5)temp.oneLineStartUp = list.at(a);  //单行注释向上添加  //<!!-   第五列
				if (a == 6)temp.oneLineEnd = list.at(a);	  //单行注释结束符号 -->      第六列
				if (a == 7)temp.endOld = list.at(a);		  //多行原结尾   */			 第七列
				if (a == 8)temp.endNow = list.at(a);		  //多行转义结尾 *$/			 第八列
				if (a == 9) {//是否包含utf8  bom文件头
					auto s = list.at(a);
					if (s == "BOM") {
						temp.BOM = true;
					}
					else {
						temp.BOM = false;
					}
				}
				if (a == 10) {
					temp.enableStr = list.at(a);     //启用标记
				}
			}
			fileinfo.insert(std::pair<QString, fileAnnotation>(temp.suffix, temp));
		}
		i++;
	}
	if (fileinfo.size() > 0) {
		nowFileInfo = fileinfo.begin()->second;
	}
	file.close();
}

/*
 * 1.设计目标:命令行输入目录遍历文本
 * 2.遍历文本
 * 3.查找 //<!-- 我是一个FreeMarker注释 --> 回车
 * 4.复制行并把前面的 //<!!- 和后面 -->删除
 * 5.查找 /*<!-- 和 -->(*)/
 * 6.复制上述内容贴到下面
 */
 /**
  * @biref  //替换单行注释 同时返回多行注释的起点终点行
  * @param all 输入全部字符串
  * @param outMap 多行注释序号 first 前面行 second 后面行 向下添加
  * @param outMapUp 多行注释序号 向上添加
  * @param one 单行序号 向下添加
  * @param oneUp 单行序号 向上添加
  */
void replaceLineStr(const QString& all,
	std::map<int, int>& outMap,
	std::map<int, int>& outMapUp,
	std::vector<int>& one,
	std::vector<int>& oneUp,
	QStringList& strList) {
	strList = all.split("\n");
	bool nowInCom = false;//当前行进入多行注释
	bool Up = false;//向上添加
	int nowStart = 0;//多行注释行前端
	int row = 0;
	int col = 0;;
	for (auto& i : strList) {
		//<!!-
		//col = i.indexOf("//<!--");
		col = i.indexOf(nowFileInfo.oneLineStartDown);
		if (col >= 0) {
			one.push_back(row);
		}
		col = i.indexOf(nowFileInfo.oneLineStartUp);
		if (col >= 0) {
			oneUp.push_back(row);
		}
		//		/*<!!-
		//col = i.indexOf("/*<!--");
		col = i.indexOf(nowFileInfo.muxLineStartDown);
		if (col >= 0) {
			nowStart = row;
			nowInCom = true;
			Up = false;
		}
		col = i.indexOf(nowFileInfo.muxLineStartUp);
		if (col >= 0) {
			nowStart = row;
			nowInCom = true;
			Up = true;
		}
		//		-->*/
		//col = i.indexOf("-->*/");
		col = i.indexOf(nowFileInfo.muxLineEnd);
		if (col >= 0 && nowInCom) {
			nowInCom = false;
			if (Up) {
				outMapUp.insert(std::pair<int, int>(nowStart, row));
			}
			else {
				outMap.insert(std::pair<int, int>(nowStart, row));
			}
		}
		row++;
	}
}

/**
 * @biref 判断当前行是否是单行注释
 * @param one 有单行注释的行列表
 * @param nowRow 当前行
 * @return 当前行是否在列表中
 */
bool isOneComLine(const std::vector<int>& one,
	int nowRow) {
	auto it = std::find(one.begin(), one.end(), nowRow);
	if (it != one.end()) {
		return true;
	}
	return false;
}
//计算替换间隔符的起点终点
bool getStartEndPoint(const QString& in, int& start, int& end) {
	start = 0;
	end = in.size();
	int col;
	//col = in.indexOf("//<!--");
	col = in.indexOf(nowFileInfo.oneLineStartDown);
	if (col >= 0) {
		start = col + nowFileInfo.oneLineStartDown.size();
	}
	col = in.indexOf(nowFileInfo.oneLineStartUp);
	if (col >= 0) {
		start = col + nowFileInfo.oneLineStartUp.size();
	}
	//col = in.indexOf("/*<!--");
	col = in.indexOf(nowFileInfo.muxLineStartDown);
	if (col >= 0) {
		int temp = col + nowFileInfo.muxLineStartDown.size();
		if (start == 0) {
			start = temp;
		}
		if (temp < start) {
			start = temp;
		}
	}
	col = in.indexOf(nowFileInfo.muxLineStartUp);
	if (col >= 0) {
		int temp = col + nowFileInfo.muxLineStartUp.size();
		if (start == 0) {
			start = temp;
		}
		if (temp < start) {
			start = temp;
		}
	}
	//col = in.indexOf("-->*/");
	col = in.indexOf(nowFileInfo.muxLineEnd);
	if (col >= 0) {
		end = col;
	}
	//col = in.indexOf("-->");
	col = in.indexOf(nowFileInfo.oneLineEnd);
	if (col >= 0) {
		end = col;
	}
	if (start == 0 && end == in.size()) {
		return false;
	}
	return true;
}

//删除间隔符
QString delSplitData(const QString& in) {
	QString out;
	out = in;
	out.replace("||", "<<<>>>");
	out.replace("|", "");
	out.replace("<<<>>>", "|");
	return out;
}
//完整的删除间隔函数
QString delSplitData2(const QString& in) {
	int start, end;//测试要删除
	if (getStartEndPoint(in, start, end)) {
		QString left = in.left(start);
		QString temp = in.mid(start, end - start);
		QString right = in.right(in.size() - end);
		QString out = left + delSplitData(temp) + right;
		return out;
	}
	else {
		QString out = delSplitData(in);
		return out;
	}
}

//添加间隔符
QString addSplitData(const QString& in) {
	QString out;
	int size = in.size();
	out.resize(size * 2);
	for (int i = 0; i < size; ++i) {
		out[i * 2] = in[i];
		out[i * 2 + 1] = QChar('|');
	}
	return out;
}

//完整的添加间隔函数
QString addSplitData2(const QString& in) {
	int start, end;
	if (getStartEndPoint(in, start, end)) {
		QString left = in.left(start);
		QString temp = in.mid(start, end - start);
		QString right = in.right(in.size() - end);
		QString out = left + addSplitData(temp) + right;
		return out;
	}
	else {
		QString out = addSplitData(in);
		return out;
	}
}
/**
 * @biref 判断单行是否包含启用标记
 * @param oldList 原文本包含的列表
 * @param row 当前行
 * @return true 表示当前行包含@ball 也就是启用标记
 */
bool includeEnable(const QStringList& oldList, int row) {
	//不启用标记
	if (nowFileInfo.enableStr.size() == 0) {
		return true;
	}
	//行数超标
	if (row >= oldList.size() && row < 0) {
		return false;
	}
	QString one = oldList[row];
	int col = one.indexOf(nowFileInfo.enableStr);
	if (col >= 0) {
		return true;
	}
	return false;
}

//判断多行是否包含启用标记
bool includeEnable(const QStringList& oldList,
	int startRow, int endRow) {
	if (nowFileInfo.enableStr.size() == 0) {
		return true;
	}
	if (startRow >= oldList.size() && startRow < 0) {
		return false;
	}
	if (endRow >= oldList.size() && endRow < 0) {
		return false;
	}
	for (int row = startRow; row <= endRow; ++row) {
		QString one = oldList[row];
		int col = one.indexOf(nowFileInfo.enableStr);
		if (col >= 0) {
			return true;
		}
	}
	return false;
}

/**
 * @biref 在新列表末尾中添加指定行.
 * @param oldList 原列表
 * @param newList 新列表
 * @param row 添加行(原列表行号)
 * @param cut 是否剔除掉注释符号
 * @param insertUp 在上方插入
 */
void replaceOneLine(const QStringList& oldList,
	QStringList& newList,
	int row,
	bool cut,
	bool insertUp = false) {
	if (oldList.size() <= row)return;
	QString nowLine = oldList[row];
	if (cut) {
		QString changeSplitLine = nowLine;
		if (nowStep == 1) {
			changeSplitLine = addSplitData2(nowLine);
		}
		else if (nowStep == 2) {
			changeSplitLine = delSplitData2(nowLine);
		}
		//nowLine.replace("//<!--", "");
		nowLine.replace(nowFileInfo.oneLineStartDown, "");
		//nowLine.replace("//<!!-", "");
		nowLine.replace(nowFileInfo.oneLineStartUp, "");
		//nowLine.replace("-->", "");
		nowLine.replace(nowFileInfo.oneLineEnd, "");
		if (nowFileInfo.enableStr.size() != 0) {
			nowLine.replace(nowFileInfo.enableStr, "");
		}
		//nowLine.replace("*$/", "*/");
		if (nowFileInfo.endNow.size() != 0) {
			nowLine.replace(nowFileInfo.endNow, nowFileInfo.endOld);
		}
		nowLine = delSplitData(nowLine);//测试要删除
		if (insertUp) {
			if (nowStep != 2) {
				if (includeEnable(oldList, row)) {
					newList.append(nowLine);
				}
			}
			newList.append(changeSplitLine);
		}
		else {
			newList.append(changeSplitLine);
			if (nowStep != 2) {
				if (includeEnable(oldList, row)) {
					newList.append(nowLine);
				}
			}
		}
	}
	else {
		newList.append(nowLine);
	}
}

/**
 * @biref 替换多行注释.
 * @param oldList 原列表
 * @param newList 新列表
 * @param startRow 起点行
 * @param endRow 终点行
 */
void replaceMuxLine(const QStringList& oldList,
	QStringList& newList,
	int startRow,
	int endRow,
	bool insertUp = false) {
	if (oldList.size() <= startRow)return;
	if (oldList.size() <= endRow)return;
	if (startRow > endRow)return;
	QStringList tempList;//未删除注释
	QStringList temp2List;//删除注释
	QString temp;
	for (int row = startRow; row <= endRow; ++row) {
		temp = oldList[row];
		QString changeSplitLine = temp;
		if (nowStep == 1) {
			changeSplitLine = addSplitData2(temp);
		}
		else if (nowStep == 2) {
			changeSplitLine = delSplitData2(temp);
		}
		tempList.append(changeSplitLine);
		if (nowFileInfo.enableStr.size() != 0) {
			temp.replace(nowFileInfo.enableStr, "");
		}
		if (row == startRow) {
			//temp.replace("/*<!--", "");
			temp.replace(nowFileInfo.muxLineStartDown, "");
			temp.replace(nowFileInfo.muxLineStartUp, "");
		}
		if (row == endRow) {
			//temp.replace("-->*/", "");
			temp.replace(nowFileInfo.muxLineEnd, "");
		}
		//temp.replace("*$/", "*/");
		if (nowFileInfo.endNow.size() != 0) {
			temp.replace(nowFileInfo.endNow, nowFileInfo.endOld);
		}
		temp2List.append(temp);
	}
	if (insertUp) {
		if (nowStep != 2) {
			if (includeEnable(oldList, startRow, endRow)) {
				newList.append(temp2List);//添加去头尾行
			}
		}
		newList.append(tempList);//添加加|转换行
	}
	else {
		newList.append(tempList);//添加加|转换行
		if (nowStep != 2) {
			if (includeEnable(oldList, startRow, endRow)) {
				newList.append(temp2List);//添加去头尾行
			}
		}
	}
}

//判断当前行是否在多行注释内部
bool isMuxComLine(const std::map<int, int>& inMap,
	const std::map<int, int>& inMapUp,
	int nowRow) {
	for (auto& i : inMap) {
		if (nowRow >= i.first && nowRow <= i.second) {
			return true;
		}
	}
	for (auto& i : inMapUp) {
		if (nowRow >= i.first && nowRow <= i.second) {
			return true;
		}
	}
	return false;
}
/**
 * @biref 判断当前行是否是多行注释首行返回尾行
 * @param inMap 多行注释起点行终点行列表
 * @param nowRow 当前行
 * @param endRow 如果是返回对应末尾行
 * @return true 当前行是多行注释的首行
 */
bool isMuxComStartLine(const std::map<int, int>& inMap, int nowRow,
	int& endRow) {
	auto i = inMap.find(nowRow);
	if (i != inMap.end()) {
		endRow = i->second;
		return true;
	}
	else {
		return false;
	}
}
void getAllFiles(QString dirPath, QStringList& fileList) {
	QDir dir(dirPath);
	// 将当前目录中的所有文件名添加到fileList
	//fileList += dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
	QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
	for (const QString& file : files) {
		fileList.push_back(dirPath + "\\" + file);
	}
	// 获取当前目录中的所有子目录
	QStringList dirList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
	// 对每一个子目录递归调用getAllFiles函数
	foreach(QString subDir, dirList) {
		getAllFiles(dir.absoluteFilePath(subDir), fileList);
	}
}

int main(int argc, char* argv[]) {
	QCoreApplication a(argc, argv);
	QStringList arguments = a.arguments();
	getFileInfo("cpp2ftl.csv");
#ifndef QT_NO_DEBUG
	for (int i = 0; i < arguments.size(); ++i) {
		qDebug() << arguments.at(i);
	}
#endif
	if (arguments.size() < 2) {
		return -1;
	}
	if (arguments.size() > 2) {
		if (arguments[2] == "/b") {//参数中添加了/b参数表示回写代码去掉注释部分的间隔符号
			nowStep = 2;
		}
	}
	QString path = arguments[1];
	QDir directory(path);
	QStringList filelist;
	if (directory.exists()) {
		getAllFiles(path, filelist);
	}
	for (auto& i : filelist) {
		QFileInfo fileInfo(i);
		QString suffix = fileInfo.suffix();
		auto atr = fileinfo.find(suffix);
		if (atr != fileinfo.end()) {
			nowFileInfo = atr->second;
		}
		QTextStream cin(stdin, QIODevice::ReadOnly);
		QTextStream cout(stdout, QIODevice::WriteOnly);
		QString strAll;
		QStringList strList;
		QFile readFile(i);
		if (readFile.open((QIODevice::ReadOnly | QIODevice::Text))) {
			QTextStream stream(&readFile);
			if (nowFileInfo.BOM) {
				stream.setGenerateByteOrderMark(true);
			}
			stream.setCodec("utf-8");
			//stream.setAutoDetectUnicode(true); //自动检测Unicode,才能正常显示文档内的汉字
			strAll = stream.readAll();
		}readFile.close();
		std::map<int, int>outMap;//多行注释行列表
		std::map<int, int>outMapUp;//多行注释行列表向上
		std::vector<int>one;//单行注释行列表
		std::vector<int>oneUp;//单行注释行列表向上
		replaceLineStr(strAll, outMap, outMapUp, one, oneUp, strList);
		QStringList  outList;
		if (outMap.size() == 0 && one.size() == 0 &&
			outMapUp.size() == 0 && oneUp.size() == 0) {
			outList = strList;
		}
		else {
			int row = 0;
			int endRow;
			for (auto& i : strList) {
				if (isMuxComStartLine(outMap, row, endRow)) {
					replaceMuxLine(strList,
						outList, row, endRow);
				}
				if (isMuxComStartLine(outMapUp, row, endRow)) {
					replaceMuxLine(strList,
						outList, row, endRow, true);
				}
				if (isMuxComLine(outMap, outMapUp, row)) {
					row++;
					continue;
				}
				if (isOneComLine(one, row)) {
					replaceOneLine(strList,
						outList, row, true);
				}
				else if (isOneComLine(oneUp, row)) {
					replaceOneLine(strList,
						outList, row, true, true);
				}
				else {
					replaceOneLine(strList,
						outList, row, false);
				}
				row++;
			}
		}
		QFile writeFile(i);
		if (writeFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
			QTextStream stream(&writeFile);
			if (nowFileInfo.BOM) {
				stream.setGenerateByteOrderMark(true);
			}
			stream.setCodec("utf-8");
			for (int i = 0; i < outList.count(); i++) {
				if (i == outList.count() - 1) {
					//最后一行不需要换行
					stream << outList.at(i);
				}
				else {
					stream << outList.at(i) << '\n';
				}
			}
			writeFile.close();
		}
	}
	return 0;
}